# encoding: utf-8
"""

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    created by lei.chang on '11/04/2025'
    comment: zego平台对接
"""
import hashlib
import requests
import json
import time
import random
from Crypto.Cipher import AES
import binascii
import struct

from project.lib.common import zego_config, qcos_config


def make_nonce():
    # 生成一个 12 字节的随机 nonce (IV)，返回字符串形式
    str_chars = '0123456789abcdefghijklmnopqrstuvwxyz'
    return ''.join(random.choice(str_chars) for _ in range(12))


def aes_gcm_with_tag_encrypy(plain_text, key, iv):
    cipher = AES.new(key.encode('utf-8'), AES.MODE_GCM, nonce=iv.encode('utf-8'))
    ciphertext, tag = cipher.encrypt_and_digest(plain_text.encode('utf-8'))
    return ciphertext, tag


class Zego:
    """
    """
    @staticmethod
    def generate_token(user_id: str,
                       effective_time_in_seconds: int = 3600,
                       payload: dict | None = None):
        """
        :param user_id:
        :param effective_time_in_seconds:
        :param payload:
        :return:
        """
        create_time = int(time.time())
        expire_time = create_time + effective_time_in_seconds
        # 生成一个随机数
        random_int = random.randint(1, 90000000)

        payload_dumps = json.dumps(payload or {})
        _token = {"app_id": zego_config.app_id,
                  "user_id": user_id,
                  "nonce": random_int,
                  "ctime": create_time,
                  "expire": expire_time, "payload": payload_dumps}
        plain_text = json.dumps(_token, separators=(',', ':'), ensure_ascii=False)

        # 生成一个12字节的用于aes 加密的once
        nonce = make_nonce()
        ciphertext, tag = aes_gcm_with_tag_encrypy(plain_text, zego_config.secret, nonce)

        # aes 的tag  需要接在buf 后面
        encrypt_buf = bytearray(len(ciphertext) + len(tag))
        encrypt_buf[0: 0 + len(ciphertext)] = ciphertext[:]
        encrypt_buf[len(ciphertext):len(ciphertext) + len(tag)] = tag[:]

        result_size = len(encrypt_buf) + 24  # 结果大小要加上加密数据的长度
        result = bytearray(result_size)

        # 处理过期时间
        big_endian_expire_time = struct.pack("!q", expire_time)
        result[0: 0 + len(big_endian_expire_time)] = big_endian_expire_time[:]

        # 处理 nonce 长度和内容
        big_endian_once_size = struct.pack("!h", len(nonce))
        result[8: 8 + len(big_endian_once_size)] = big_endian_once_size[:]

        buffer = bytearray(nonce.encode('utf-8'))
        result[10: 10 + len(buffer)] = buffer[:]

        # 处理加密数据
        big_endian_buf_size = struct.pack("!h", len(encrypt_buf))
        result[22: 22 + len(big_endian_buf_size)] = big_endian_buf_size[:]
        result[24: 24 + len(encrypt_buf)] = encrypt_buf[:]

        # append mode token use gcm
        AesEncryptModeGCM = 1
        result.append(AesEncryptModeGCM)

        # 将结果进行 Base64 编码
        token = "04" + binascii.b2a_base64(result, newline=False).decode()

        return token

    @staticmethod
    def get_common_params():
        """ 获取 API请求的公共参数
        :return:
        """
        ctime_ts = int(time.time())
        signature_nonce = make_nonce()
        signature = Zego.generate_signature(zego_config.app_id, signature_nonce, zego_config.secret, ctime_ts)
        query_params = {
            'AppId': zego_config.app_id,
            'Signature': signature,
            'SignatureNonce': signature_nonce,
            'SignatureVersion': '2.0',
            'Timestamp': ctime_ts
        }

        return query_params

    @staticmethod
    def generate_signature(app_id: int, signature_nonce: str, server_secret: str, timestamp: int):
        """
        :param app_id:
        :param signature_nonce:
        :param server_secret:
        :param timestamp:
        :return:
        """
        str1 = str(app_id) + signature_nonce + server_secret + str(timestamp)
        hash = hashlib.md5()
        hash.update(str1.encode("utf8"))
        signature = hash.hexdigest()

        return signature

    @staticmethod
    def start_recording(room_id: str):
        """
        :return:
        """
        url = 'https://cloudrecord-api.zego.im/?Action=StartRecord'

        query_params = Zego.get_common_params()
        body_params = {
            'RoomId': room_id,
            'RecordInputParams': {
                'RecordMode': 2,
                'StreamType': 3,
                "MaxIdleTime": 60,
                "MixConfig": {
                    "MixMode": 2,
                    "MixOutputStreamId": "mix",
                    "MixOutputVideoConfig": {
                        "Width": 1280,
                        "Height": 720,
                        "Fps": 15,
                        "Bitrate": 1130000
                    }
                }
            },
            "RecordOutputParams": {
                "OutputFileFormat": "mp4",
                "OutputFolder": "/"
            },
            "StorageParams": {
                "Vendor": 3,
                "Region": qcos_config.region,
                "Bucket": qcos_config.bucket,
                "AccessKeyId": qcos_config.secret_id,
                "AccessKeySecret": qcos_config.secret_key
            }
        }
        response = requests.post(url, json=body_params, params=query_params)
        if response.status_code != 200:
            raise Exception('录制音视频失败。')

        result = response.json()
        if 'Data' not in result:
            raise Exception(f'开启录制视频失败。 code: {result["Code"]} message: {result["Message"]}')

        task_id = result['Data']['TaskId']
        return task_id

    @staticmethod
    def stop_recording(task_id: str):
        """
        :param task_id:
        :return:
        """
        url = 'https://cloudrecord-api.zego.im/?Action=StopRecord'
        query_params = Zego.get_common_params()
        body_params = {
            'TaskId': task_id
        }
        response = requests.post(url, json=body_params, params=query_params)
        if response.status_code != 200:
            raise Exception('录制音视频失败。')


if __name__ == "__main__":
    """
    """
    token = Zego.generate_token(
        user_id="user_01",
        effective_time_in_seconds=3600
    )
    #
    print(token)

    # task_id = Zego.start_recording('12345')
    # print(task_id)

    # Zego.stop_recording('aRFNoDQC-gtahVkO')


